package com.fourhats.controller;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.imageio.ImageIO;

import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fourhats.exception.ProductImageQuantityException;
import com.fourhats.model.ObjectTypes;
import com.fourhats.services.CategoryService;
import com.fourhats.services.EmailService;
import com.fourhats.services.MarketService;
import com.fourhats.services.ProductService;
import com.fourhats.services.QuestionService;
import com.fourhats.services.UserService;
import com.fourhats.services.VisitEntityService;
import com.fourhats.services.dto.CategoryDto;
import com.fourhats.services.dto.ImageDto;
import com.fourhats.services.dto.MarketDto;
import com.fourhats.services.dto.ProductBuyedDto;
import com.fourhats.services.dto.ProductDto;
import com.fourhats.services.dto.UserDto;
import com.fourhats.services.util.FileHelper;
import com.fourhats.services.util.ImageInfo;
import com.fourhats.services.util.Page;
import com.google.gson.Gson;

@Controller
public class ProductController extends GenericController {

	@Autowired
	private ProductService productService;
	
	@Autowired
	private CategoryService categoryService;
	
	@Autowired
	private MarketService marketService;
	
	@Autowired
	private EmailService emailService;
	
	@Autowired
	private UserService userService;
	
	@Autowired
	private VisitEntityService entityVisitService;
	
	@Autowired
	private QuestionService questionService;
	
	@Value("#{configProps}")
	private Properties properties;
	
	private final String SMALLPRODUCTFOLDER = "small";
	private final String MEDIUMPRODUCTFOLDER = "medium";
	private final String LARGEPRODUCTFOLDER = "large";
	private final String ORIGINAL_FOLDER = "originals";
	
	/*Temp*/
	private final String ORIGINAL_TEMP_FOLDER = "originals";
	
	@RequestMapping("/product/detail/{productId}/{productName}")
    public String goToProductDetail(@PathVariable long productId, @PathVariable String productName, Model model) {
		ProductDto product = productService.getProductById(productId);
		boolean isOwner = product.getUserId().equals(this.getUserId());
		
		if(product == null || !product.isEnable()) {
			return "redirect:/home";	
		}
		
		if(!isOwner) {
			this.entityVisitService.addVisitEntity(ObjectTypes.PRODUCT.getValue(), productId, this.getUserId());
			if(!product.isMarketEnable()) {
				return "redirect:/home";
			}
		}
		
		Gson gson = new Gson();
		model.addAttribute("product", gson.toJson(product).toString());
        model.addAttribute("isOwner", isOwner);
        model.addAttribute("questions", gson.toJson(questionService.getQuestionsWithAnswers(productId, ObjectTypes.PRODUCT.getValue(), 1)).toString());
        
        return "content/productDetail";
	}
	
	@RequestMapping("/product/productCreation")
    public String goToCreateProduct(ModelMap model) {
		MarketDto market;
		
		try {
			market = marketService.getMarketByUser(super.getUserId());
			if (market == null) {
				return "redirect:/home";
			}
		} catch (Exception e) {
			return "redirect:/home";
		}
		
		List<CategoryDto> categories = categoryService.getCategories();
		Gson gson = new Gson();
		model.addAttribute("market", gson.toJson(market));
		model.addAttribute("categories", gson.toJson(categories));
		return "content/productCreation";
	}
	
	@RequestMapping("/product/productEdition/{productId}")
    public String goToEditProduct(@PathVariable long productId, ModelMap model) {
		MarketDto market;
		
		try {
			market = marketService.getMarketByUser(super.getUserId());
			if (market == null) {
				return "redirect:/home";
			}
		} catch (Exception e) {
			return "redirect:/home";
		}
		
		List<CategoryDto> categories = categoryService.getCategories();
		ProductDto product = productService.getProductById(productId);
		if(!product.getUserId().equals(super.getUserId())) {
			return "redirect:/home";
		}
		
		Gson gson = new Gson();
		model.addAttribute("market", gson.toJson(market));
		model.addAttribute("categories", gson.toJson(categories));
		model.addAttribute("product", gson.toJson(product));
		
		return "content/productCreation";
	}
	
	@RequestMapping(value="/product/createProduct", method= RequestMethod.POST)
	public @ResponseBody String createProduct(@RequestBody ProductDto product, Model model) throws Exception {
		String tempPath = properties.getProperty("folder.temp");
		String productPath = properties.getProperty("folder.product");
		
		if(product.getImages().size() > 4) {
			throw new ProductImageQuantityException();
		}
		
		productService.addProduct(product);

		copyImageFromTemp(product.getImages(), tempPath, productPath);
		
		model.addAttribute("successProductCreation", true);
		return "redirect:/user/profile";
	}
	
	@RequestMapping(value="/product/editProduct", method= RequestMethod.POST)
	public @ResponseBody String editProduct(@RequestBody ProductDto product, Model model) throws Exception {
		if(!product.getUserId().equals(super.getUserId())) {
			return "redirect:/Home";
		}

		String tempPath = properties.getProperty("folder.temp");
		String productPath = properties.getProperty("folder.product");
		
		if(product.getImages().size() > 4){
			throw new ProductImageQuantityException();
		}
		
		productService.editProduct(product);
		
		List<ImageDto> images = new ArrayList<ImageDto>();
		for(ImageDto img : product.getImages()){
			if(img.getId() == 0){
				images.add(img);
			}
		}
		
		copyImageFromTemp(images, tempPath, productPath);
		
		model.addAttribute("successProductEdition", true);
		return "redirect:/user/profile";
	}

	@RequestMapping(value="/product/deleteProduct")
	public @ResponseBody void deleteProduct(long productId) {
		ProductDto product = this.productService.getProductById(productId);
		if(!product.getUserId().equals(super.getUserId())) {
			return;
		}

		productService.deleteProduct(productId);
	}
	
	@RequestMapping(value="/products")
    public String goToProducts(Model model) {
		return goToProductsWithPreselectedCategory(0, model);
    }
	
	@RequestMapping(value="/product/products/{categoryId}")
    public String goToProductsWithPreselectedCategory(@PathVariable long categoryId, Model model) {
		model.addAttribute("categoryId", categoryId);
		if(categoryId == 0) {
			model.addAttribute("categoryName", "Todos");	
		} else {
			model.addAttribute("categoryName", categoryService.getCategoryById(categoryId).getName());
		}
		
		return "content/products";
    }
	
	@RequestMapping(value = "/product/getCategories", 
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody List<CategoryDto> getCategories() {
		List<CategoryDto> categories = categoryService.getCategories();
		return categories;
	}
	
	@RequestMapping(value = "/product/getProductsByCategory", 
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody Page<ProductDto> getProductsByCategory(long categoryId, int pageNumber, int pageSize, String orderBy) {
		Page<ProductDto> products;
		
		if(categoryId == 0) {
			products = productService.getAllProducts(pageNumber, pageSize, orderBy);
		} else {
			products = productService.getProductsByCategory(categoryId, pageNumber, pageSize, orderBy);
		}
		
		return products;
	}
	
	@RequestMapping(value = "/product/getProductsByMarketId", 
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody Page<ProductDto> getProductsByMarketId(long marketId, int pageNumber, int pageSize) {
		Page<ProductDto> products = productService.getProductsByMarketOrdered(marketId, pageNumber, pageSize);
		return products;
	}
	
	@RequestMapping(value = "/product/getProductsByEnabledOrDisabledMarketId", 
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody Page<ProductDto> getProductsByEnabledOrDisabledMarketId(long marketId, int pageNumber, int pageSize) {
		Page<ProductDto> products = productService.getProductsByMarket(marketId, pageNumber, pageSize);
		return products;
	}
	
	@RequestMapping(value = "/product/getProductById", 
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody ProductDto getProductById(long productId) {
		return productService.getProductById(productId);
	}

	@RequestMapping(value = "/product/getProductsPageByKeyword", 
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody Page<ProductDto> getProductsPageByKeyword(String keyword, int pageNumber, int pageSize) {
		return this.productService.getProductsPageByKeyword(keyword, pageNumber, pageSize );
	}
	
	@RequestMapping(value="/product/canPurchaseProduct",
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody String canPurchaseProduct() {
		super.verifyAuthentication();
		return super.SUCCESS_MESSAGE;
	}
	
	@RequestMapping(value="/product/purchaseProduct",
	method = RequestMethod.GET, 
	headers="Accept=*/*")
	public @ResponseBody String purchaseProduct(long productId) {
		super.verifyAuthentication();
		boolean couldUserBuy = productService.purchaseProduct(productId, super.getUserId());

		if(couldUserBuy) {
			UserDto buyer = this.userService.getUserById(super.getUserId());
			ProductDto product = this.productService.getProductById(productId);
			Map<String, String> valuesToBuyer = new HashMap<String, String>();
			valuesToBuyer.put("{UserName}", buyer.getName());
			valuesToBuyer.put("{ProductName}", product.getName());
			valuesToBuyer.put("{MarketName}", product.getMarketName());
			valuesToBuyer.put("{Email}", product.getMarketEmail());
			valuesToBuyer.put("{Phone}", product.getMarketPhone());
			valuesToBuyer.put("{Address}", product.getMarketAddress());
			
			Map<String, String> valuesToSeller = new HashMap<String, String>();
			valuesToSeller.put("{UserName}", product.getUserName());
			valuesToSeller.put("{ProductName}", product.getName());
			valuesToSeller.put("{BuyerName}", buyer.getFullName());
			valuesToSeller.put("{Email}", buyer.getEmail());
			valuesToSeller.put("{Phone}", buyer.getPhone());
			valuesToSeller.put("{Address}", buyer.getAddress());
			
			try {
				this.emailService.sendEMail(buyer.getEmail(), "Compraste un producto sustentable", this.properties.getProperty("folder.emails"), "successPurchaseEmail", valuesToBuyer);
				this.emailService.sendEMail(product.getMarketEmail(), "Vendiste un producto sustentable", this.properties.getProperty("folder.emails"), "successSellingEmail", valuesToSeller);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			return super.SUCCESS_MESSAGE;
		} else {
			return "BUYING_ERROR";
		}
	}
	
	@RequestMapping(value = "/product/getPurchasedProducts", method = RequestMethod.GET, headers="Accept=*/*")
	public @ResponseBody Page<ProductBuyedDto> getPurchasedProducts(long userId, int pageNumber, int pageSize) {
		return productService.getPurchasedProducts(userId, pageNumber, pageSize);
	}
	
	@RequestMapping(value = "/product/getSoldProducts", method = RequestMethod.GET, headers="Accept=*/*")
	public @ResponseBody Page<ProductBuyedDto> getSoldProducts(long userId, int pageNumber, int pageSize) {
		return productService.getSoldProducts(userId, pageNumber, pageSize);
	}
	
	private void copyImageFromTemp(List<ImageDto> images, String tempPath,String targetPath) throws IOException, Exception,FileNotFoundException {
		for(ImageDto image : images){
			List<ImageInfo> imagesInfo = new ArrayList<ImageInfo>();
			
			BufferedImage imageSrc = null;
			try{
				File file = new File(tempPath, image.getPath());
				imageSrc = ImageIO.read(file);
				
				int smallWidth = imageSrc.getWidth() < 125 ? imageSrc.getWidth() : 125;
				int smallHeight = imageSrc.getHeight() < 100 ? imageSrc.getHeight() : 100;
				
				int mediumWidth = imageSrc.getWidth() < 410 ? imageSrc.getWidth() : 410;
				int mediumHeight = imageSrc.getHeight() < 270 ? imageSrc.getHeight() : 270;
				
				imagesInfo.add(new ImageInfo(SMALLPRODUCTFOLDER, smallWidth, smallHeight));
				imagesInfo.add(new ImageInfo(MEDIUMPRODUCTFOLDER, mediumWidth, mediumHeight));
				
				byte [] imageBytes = FileHelper.bufferedImageToByte(imageSrc, FilenameUtils.getExtension(image.getPath()));
				imagesInfo.add(new ImageInfo(LARGEPRODUCTFOLDER, imageSrc.getWidth(), imageSrc.getHeight()));
				FileHelper.generateImages2(imageBytes, targetPath, image.getPath(), imagesInfo);
				
				String src = FileHelper.getFullPath(tempPath, ORIGINAL_TEMP_FOLDER, image.getPath());
				String dst = FileHelper.getFullPath(targetPath, ORIGINAL_FOLDER, image.getPath());
				FileHelper.copyFile(src, dst);
			}catch(Exception e){
				throw e;
			}finally{
				if(imageSrc != null){
					imageSrc.flush();
				}
			}
		}
	}
	
}